package Server;

import Server.Database.Database;
import java.net.ServerSocket;
import java.net.Socket;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;

/**
 * Listens for incoming connections and acts as a gateway connecting the {Server, Database, ClientHandler} component
 * with {Client, Project, Listener, Sender} component.
 * @author PZK
 *
 */
public class ClientHandler {

	/**
	 * Client list. Use synchronized wrapper ONLY with multiple operations, i.e.
	 * ITERATOR.
	 */
	private List<Client> clients = Collections
			.synchronizedList(new ArrayList<Client>());

	private ArrayList<Client> clientQueue = new ArrayList<Client>();

	private ArrayList<Project> projects = new ArrayList<Project>();

	private ArrayList<String> allUsers = new ArrayList<String>();

	private ArrayList<String> offlineUsers;
        private Database database;
        private ArrayList<String> filters;
        public ClientHandler(ServerSocket serverSocket, Database database) {
                this.database = database;
                try{
                    filters = database.phraseRegister.getAllPhrazes();
                } catch (Exception e){
                    e.printStackTrace();
                }
		projects = getAllProjects();

		allUsers = getAllUsers();

		offlineUsers = getOfflineUsers();
		printOfflineUsers("GET_OFFLINE_USERS");
		
                while (true) {
                    
			try {
                               
				Socket clientSocket = serverSocket.accept();
                                addClientToQueue(clientSocket);
                                
			} catch (Exception e) {
                                System.out.println("Closing all connections...");
				//e.printStackTrace();
                                break;
 			}
		}
                

	}

	private ArrayList<Project> getAllProjects() {

		ArrayList<Project> result = new ArrayList<Project>();

		try {
			result = database.projectRegister.getAllProjects();
		} catch (SQLException e) {
			e.printStackTrace();
		}

		return result;

	}

	private ArrayList<String> getAllUsers() {

		ArrayList<String> result = new ArrayList<String>();

		try {
			result = database.usersRegister.getAllUsers();
		} catch (SQLException e) {
			e.printStackTrace();
		}

		return result;

	}

	private ArrayList<String> getOfflineUsers() {

		return new ArrayList<String>(allUsers);

	}
	private void printOfflineUsers(String merp){
		System.out.print("OFFLINE USERS("+merp+" ["+offlineUsers.size()+"]): ");
		for(String temp:offlineUsers)
			System.out.print(temp);
		System.out.println();
	}
	public void addClientToQueue(Socket clientSocket) {

		System.out.println("ADD CLIENT TO QUEUE");
		synchronized (clientQueue) {
			clientQueue.add(new Client(clientSocket, this));
		}
		printOfflineUsers("ADD_CLIENT_TO_QUEUE");

	}

	public synchronized void addClient(Client client, String name) {

			System.out.println("ADD CLIENT: " + name);
			offlineUsers.remove(name);
			clientQueue.remove(client);
			clients.add(client);
			sendOfflineUsersToAll();
			userLoggedIn(name);
			printOfflineUsers("ADD_CLIENT");

	}

	public synchronized void removeClient(Client client) {

			System.out.println("SYSTEM: REMOVED USER -> " + client.getName());
			userLoggedOut(client.getName());
			offlineUsers.add(client.getName());

			client.interrupt();
			clients.remove(client);
			clientQueue.remove(client);

			sendOfflineUsersToAll();
			printOfflineUsers("REMOVE_CLIENT");
		/*
		 * synchronized(clients) { Iterator<Client> i = clients.iterator();
		 * while (i.hasNext()){ if(i.next().equals(client)){ i.remove(); } } }
		 */
	}

	public synchronized void processMessage(Client client, String message) {

		if (message.contains(client.getSystemCode())) {
			message = message.substring(client.getSystemCode().length()
					+ client.getRegexCode().length());
			String[] split = message.split(client.getRegexCode());
			switch (split[0]) {
			case "LOGIN": {
				if (Pattern.matches("[a-zA-Z]+", split[1])
						&& Pattern.matches("[a-zA-Z]+", split[2])
						&& userExists(split[1])) {
					try {
						if (database.usersRegister.login(split[1], split[2])) {
                                                        if(!database.usersRegister.isUserBlocked(split[1])) {
                                                            client.send(client.getSystemCode()
                                                                            + client.getRegexCode() + "LOGIN"
                                                                            + client.getRegexCode() + "TRUE");
                                                            client.setName(split[1]);
                                                            if (database.usersRegister.isAdmin(split[1])) {
                                                                client.send(client.getSystemCode()
                                                                            + client.getRegexCode() + "ADMIN"
                                                                            + client.getRegexCode() + "TRUE");
                                                            }
                                                        }
                                                        else {
                                                            client.send(client.getSystemCode()
                                                                    + client.getRegexCode() + "BLOCKED"
                                                                    + client.getRegexCode() + "TRUE");
                                                        }
						} else {
							client.send(client.getSystemCode()
									+ client.getRegexCode() + "LOGIN"
									+ client.getRegexCode() + "FALSE");
						}
					} catch (SQLException e) {
						e.printStackTrace();
					}
				}
			}
				break;
			case "GET_ALL_PROJECTS": {
				sendAllProjects(client);
			}
				break;
			case "GET_ALL_USERS": {
				sendAllUsers(client);
				addClient(client, client.getName());
			}
				break;
			case "GET_PROJECT_USERS": {
				sendProjectUsers(client, split[1]);
			}
				break;
			case "GET_OFFLINE_USERS": {
				sendOfflineUsers(client);
			}
				break;
			case "SEND_MESSAGE_TO_CLIENT": {
                                String extendedMessage = client.getName();
				if (split.length >= 3) {
					extendedMessage += ": " + split[2];
				} else
					extendedMessage += " says an empty string :(";
                                if (split[3].equals("TRUE")) {
                                    try {
                                        database.messagesRegister.saveMessage(extendedMessage);
                                    } catch (SQLException ex) {
                                        Logger.getLogger(ClientHandler.class.getName()).log(Level.SEVERE, null, ex);
                                    }
                                }
				sendMessageToClient(client, split[1], extendedMessage);
			}
				break;
			case "SEND_MESSAGE_TO_PROJECT": {
				String extendedMessage = client.getName();
				if (split.length >= 3) {
					extendedMessage += ": " + split[2];
				} else
					extendedMessage += " says an empty string :(";
                                if (split[3].equals("TRUE")) {
                                    try {
                                        database.messagesRegister.saveMessage(extendedMessage);
                                    } catch (SQLException ex) {
                                        Logger.getLogger(ClientHandler.class.getName()).log(Level.SEVERE, null, ex);
                                    }
                                }
				sendMessageToProject(split[1], extendedMessage);
			}
				break;
			default: {
				System.out.println("SERVER: UNKNOWN SYSTEM COMMAND: "
						+ split[0]);
			}
				break;
			}
		} else {
			System.out.println(message);
		}

	}

	private boolean userExists(String username) {

		boolean result = false;

		for (Project temp : projects) {
			if (temp.containsUser(username)) {
				result = true;
				break;
			}
		}

		return result;

	}
        private String applyFilter(String msg){
            System.out.println("Filtru: "+filters.size());
            for(int i = 0; i < filters.size(); i++){
                System.out.println(filters.get(i));
                msg = msg.replaceAll(filters.get(i), "***");
            }
            return msg;
        }
	private void sendMessageToClient(Client source, String subject,
			String message) {

		
		for (Client client : clients) {

			if (client.getName().equals(source.getName())) {
				sentToClient(
						client,
						client.getSystemCode() + client.getRegexCode()
								+ "SEND_MESSAGE_TO_CLIENT"
								+ client.getRegexCode() + subject
								+ client.getRegexCode() + applyFilter(message));
			} else if (client.getName().equals(subject)) {
				sentToClient(
						client,
						client.getSystemCode() + client.getRegexCode()
								+ "SEND_MESSAGE_TO_CLIENT"
								+ client.getRegexCode() + source.getName()
								+ client.getRegexCode() + applyFilter(message));
			}

		}

	}

	private void sendMessageToProject(String subject, String message) {

		for (Project temp : projects) {

			if (temp.getName().equals(subject)) {

				ArrayList<String> clientsStr = temp.getClients();
				for (String clientName : clientsStr) {
					for (Client client : clients) {

						if (client.getName().equals(clientName)) {
							sentToClient(
									client,
									client.getSystemCode()
											+ client.getRegexCode()
											+ "SEND_MESSAGE_TO_PROJECT"
											+ client.getRegexCode() + subject
											+ client.getRegexCode() + applyFilter(message));
							break;
						}

					}
				}
				break;
			}

		}

	}
	
	private void userLoggedIn(String name){
		
		String message = "[SYSTEM_MESSAGE] "+name+" prisijunge prie sistemos.";
		for(Project temp:projects){
			
			if(temp.containsUser(name)){
				sendMessageToProject(temp.getName(), message);
			}
			
		}
		
	}
	
	private void userLoggedOut(String name){
		
			String message = "[SYSTEM_MESSAGE] "+name+" atsijunge nuo sistemos.";
			for(Project temp:projects){
			
			if(temp.containsUser(name)){
				sendMessageToProject(temp.getName(), message);
			}
			
		}
		
	}
	
	private void sendAllProjects(Client client) {

		String message = client.getSystemCode() + client.getRegexCode()
				+ "GET_ALL_PROJECTS";
                //Is naujo surenka projektu lista
                projects = getAllProjects();
		if (!projects.isEmpty()) {
			for (int i = 0; i < projects.size(); i++) {
				if (projects.get(i).containsUser(client.getName())) {
					message += client.getRegexCode();
					message += projects.get(i).getName();
				}
			}
			client.send(message);
		} else
			System.out.println("NO PROJECTS!");

	}

	private void sendAllUsers(Client client) {

		String message = client.getSystemCode() + client.getRegexCode()
				+ "GET_ALL_USERS";
                //ius naujo nustato userius
                allUsers = getAllUsers();
		if (!allUsers.isEmpty()) {
			for (int i = 0; i < allUsers.size(); i++) {
				message += client.getRegexCode();
				message += allUsers.get(i);
			}
			client.send(message);
		} else
			System.out.println("NO USERS!");

	}

	private void sendOfflineUsers(Client client) {

		String message = client.getSystemCode() + client.getRegexCode()
				+ "GET_OFFLINE_USERS";

		if (!allUsers.isEmpty()) {

			for (String temp : offlineUsers) {

				message += client.getRegexCode();
				message += temp;

			}
			client.send(message);
		} else
			System.out.println("NO USERS!");

	}

	private void sendOfflineUsersToAll() {

		if (clients != null) {
			if (clients.size() > 0) {
				for (Client temp : clients) {
					sendOfflineUsers(temp);
				}
			}
		}

	}

	private void sendProjectUsers(Client client, String project) {

		String message = client.getSystemCode() + client.getRegexCode()
				+ "GET_PROJECT_USERS";

		if (!projects.isEmpty()) {
			for (int i = 0; i < projects.size(); i++) {
				if (projects.get(i).getName().contains(project)) {
					ArrayList<String> clients = projects.get(i).getClients();
					for (String temp : clients) {
						message += client.getRegexCode();
						message += temp;
					}
					break;
				}
			}
			client.send(message);
		} else
			System.out.println("NO PROJECTS!");

	}

	public void sentToClient(Client client, String message) {

		if (client.isLoggedIn())
			client.send(message);

	}

}
